home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1999 January: Mac OS SDK / Dev.CD Jan 99 SDK1.toast / Development Kits / Installer SDK 1.2 / Upgrader 1.2.1 & Engines / Upgrader 1.2.1 / Plug-in Examples / Common Files / Editor Utilities / LMenuBar.cp < prev    next >
Encoding:
Text File  |  1998-08-25  |  14.5 KB  |  494 lines  |  [TEXT/CWIE]

  1. // ===========================================================================
  2. //    LMenuBar.cp                   ©1993-1996 Metrowerks Inc. All rights reserved.
  3. // ===========================================================================
  4. //
  5. //    Manages a Mac menu bar. Contains a list a Menu objects.
  6. //
  7. //    Resources:
  8. //        'MBAR'        Standard Mac resource for a Menu Bar
  9. //
  10. //    •• Techniques
  11. //
  12. //    • Adding/Removing a Menu depending on runtime conditions
  13. //        During initialization, create a Menu object
  14. //        Store pointer to Menu object in an appropriate place
  15. //        When you want to add the Menu to the MenuBar:
  16. //            LMenuBar::GetCurrentMenuBar()->InstallMenu(myMenu, beforeID);
  17. //            where "myMenu" is the pointer to the Menu object
  18. //                "beforeID" is the ID of the Menu before which to put
  19. //                this Menu (use InstallMenu_AtEnd to put the Menu last or
  20. //                the Toolbox constant hierMenu for a hierarchical or popup)
  21. //        When you want to remove the Menu from the MenuBar:
  22. //            LMenuBar::GetCurrentMenuBar()->RemoveMenu(myMenu);
  23. //
  24. //    • Toggling a Menu item
  25. //        For a menu item that toggles between two states (such as Show Toolbar
  26. //        and Hide Toolbar), you can change the command as well as the text
  27. //        of the menu item.
  28. //
  29. //        The follow code fragment assumes that you have declared oldCommand
  30. //        and newCommand as CommandT variables or constants and newName as
  31. //        as some kind of string.
  32. //                                        
  33. //        ResIDT        theID;
  34. //        MenuHandle    theMenuH;
  35. //        Int16        theItem;            // Locate oldCommand
  36. //        FindMenuItem(oldCommand, theID, theMenuH, theItem);
  37. //        if (theItem != 0) {                // Replace with newCommand
  38. //            LMenu    *theMenu = LMenuBar::GetCurrentMenuBar->FetchMenu(theID);
  39. //            theMenu->SetCommand(theItem, newCommand);
  40. //            SetMenuItemText(theMenuH, theItem, newName);
  41. //        }
  42.         
  43. #ifdef PowerPlant_PCH
  44. #include PowerPlant_PCH
  45. #endif
  46.  
  47. #include <LMenuBar.h>
  48. #include <LMenu.h>
  49.  
  50. #include <UMemoryMgr.h>
  51. #include <PP_Resources.h>
  52. #include <PP_Messages.h>
  53.  
  54. #ifndef __TOOLUTILS__
  55. #include <ToolUtils.h>
  56. #endif
  57.  
  58. // === Static Members ===
  59.  
  60. LMenuBar*    LMenuBar::sMenuBar;
  61.  
  62.  
  63. // ---------------------------------------------------------------------------
  64. //        • LMenuBar
  65. // ---------------------------------------------------------------------------
  66. //    Construct Menu Bar from a MBAR resource
  67.  
  68. LMenuBar::LMenuBar(
  69.     ResIDT    inMBARid )
  70. {
  71.     StResource    theMBAR('MBAR', inMBARid);
  72.     ::HLockHi(theMBAR.mResourceH);
  73.     
  74.     sMenuBar = this;
  75.     mMenuListHead = nil;
  76.                                     // Install each menu in the MBAR resource
  77.     Int16    *menuIDP = (Int16 *) *theMBAR.mResourceH;
  78.     Int16    numMenus = *menuIDP++;
  79.     for (Int16 i = 1; i <= numMenus; ++i) {
  80.         InstallMenu(new LMenu(*menuIDP++), InstallMenu_AtEnd);
  81.     }
  82.  
  83.     // Populate the Apple Menu
  84.     MenuHandle    macAppleMenuH = ::GetMenuHandle(MENU_Apple);
  85.     if (macAppleMenuH != nil) {
  86.         // Add Apple menu items only if we haven' before, based on the number of items in the menu
  87.         if( ::CountMItems(macAppleMenuH) < 5 )
  88.             ::AppendResMenu(macAppleMenuH, 'DRVR');
  89.     }
  90.  
  91.     ::InvalMenuBar();
  92.     
  93. }
  94.  
  95.  
  96. // ---------------------------------------------------------------------------
  97. //        • ~LMenuBar
  98. // ---------------------------------------------------------------------------
  99. //    Destructor
  100.  
  101. LMenuBar::~LMenuBar()
  102. {
  103.     LMenu    *theMenu = nil;                // Loop through all Menus
  104.     while (FindNextMenu(theMenu)) {
  105.         
  106.         
  107.         // Code lifted from RemoveMenu(theMenu);            
  108.         LMenu    *theTempoMenu = mMenuListHead;
  109.         LMenu    *prevMenu = nil;
  110.         while ((theTempoMenu != nil) && (theTempoMenu != theMenu)) {
  111.             prevMenu = theTempoMenu;
  112.             theTempoMenu = theTempoMenu->GetNextMenu();
  113.         }
  114.         
  115.         if (theTempoMenu != nil) {            // Menu is in our list
  116.                                         // Remove it from our singly-linked list
  117.             if (prevMenu == nil) {
  118.                 mMenuListHead = theMenu->GetNextMenu();
  119.             } else {
  120.                 prevMenu->SetNextMenu(theMenu->GetNextMenu());
  121.             }
  122.             theMenu->SetNextMenu(nil);
  123.             theMenu->SetInstalled(false);
  124.         }
  125.  
  126.  
  127.         theMenu->mMacMenuH = nil;        // Prevents actual menu from being removed
  128.         delete theMenu;                    // Delete Menu object
  129.         theMenu = nil;
  130.     }
  131.  
  132. }
  133.  
  134.  
  135. // ---------------------------------------------------------------------------
  136. //        • MenuCommandSelection
  137. // ---------------------------------------------------------------------------
  138. //    Handle menu selection with the Mouse and return the command number for
  139. //    the item chosen
  140. //
  141. //    When to Override:
  142. //        To implement alternative menu selection behavior.
  143. //        To change menu commands based on what modifier keys are down
  144.  
  145. CommandT
  146. LMenuBar::MenuCommandSelection(
  147.     const EventRecord    &inMouseEvent,
  148.     Int32                &outMenuChoice) const
  149. {
  150.     outMenuChoice = ::MenuSelect(inMouseEvent.where);
  151.     
  152.     Int32    menuCmd = cmd_Nothing;
  153.     if (HiWord(outMenuChoice) != 0) {
  154.         menuCmd = FindCommand(HiWord(outMenuChoice), LoWord(outMenuChoice));
  155.     }
  156.     
  157.     return menuCmd;
  158. }
  159.  
  160.  
  161. // ---------------------------------------------------------------------------
  162. //        • CouldBeKeyCommand
  163. // ---------------------------------------------------------------------------
  164. //    Return whether the keystoke could be a key equivalent for a menu command
  165. //
  166. //    When to Override:
  167. //        To implement keyboard equivalents that use modifier keys other than
  168. //        just the command key. This function returns true if the command
  169. //        key is down.
  170.  
  171. Boolean
  172. LMenuBar::CouldBeKeyCommand(
  173.     const EventRecord    &inKeyEvent) const
  174. {
  175.     return (inKeyEvent.modifiers & cmdKey) != 0;
  176. }
  177.  
  178.  
  179. // ---------------------------------------------------------------------------
  180. //        • FindKeyCommand
  181. // ---------------------------------------------------------------------------
  182. //    Return the Command number corresponding to a keystroke
  183. //        Returns cmd_Nothing if the keystroke is not a menu equivalent
  184. //
  185. //    Usage Note: Call this function when CouldBeKeyCommand() is true.
  186. //
  187. //    When to Override:
  188. //        To implement keyboard equivalents that use modifier keys other than
  189. //        just the command key. This function calls the Toolbox routine
  190. //        MenuKey to find the associated menu item, if any. Override this
  191. //        function (as well as CouldBeKeyCommand) to implement key equivalents
  192. //        that use other modifier keys, such as Option, Shift, or Control.
  193.  
  194. CommandT
  195. LMenuBar::FindKeyCommand(
  196.     const EventRecord    &inKeyEvent,
  197.     Int32                &outMenuChoice) const
  198. {
  199.     CommandT    menuCmd = cmd_Nothing;
  200.     
  201.     char    theChar = inKeyEvent.message & charCodeMask;
  202.     outMenuChoice = ::MenuKey(theChar);
  203.     if (HiWord(outMenuChoice) != 0) {
  204.         menuCmd = FindCommand(HiWord(outMenuChoice), LoWord(outMenuChoice));
  205.     }
  206.     
  207.     return menuCmd;
  208. }
  209.  
  210.  
  211. // ---------------------------------------------------------------------------
  212. //        • FindCommand
  213. // ---------------------------------------------------------------------------
  214. //    Return the Command number corresponding to a Menu (ID, item) pair
  215.  
  216. long
  217. LMenuBar::FindCommand(
  218.     ResIDT    inMENUid,
  219.     Int16    inItem) const
  220. {
  221.                                     // Start with synthetic command number
  222.     CommandT    theCommand = -(((Int32)inMENUid) << 16) - inItem;
  223.  
  224.     LMenu    *theMenu = mMenuListHead;
  225.     while (theMenu) {                // Search all installed Menus
  226.         if (inMENUid == theMenu->GetMenuID()) {
  227.             theCommand = theMenu->CommandFromIndex(inItem);
  228.             break;
  229.         }
  230.         theMenu = theMenu->GetNextMenu();
  231.     }
  232.     
  233.     return theCommand;
  234. }
  235.  
  236.  
  237. // ---------------------------------------------------------------------------
  238. //        • FindMenuItem
  239. // ---------------------------------------------------------------------------
  240. //    Passes back the MENU id, MenuHandle, and item number corresponding to a
  241. //    Command number
  242. //
  243. //    If the Command is not associated with any item in the MenuBar,
  244. //        outMENUid is 0, outMenuHandle is nil, and outItem is 0
  245.  
  246. void
  247. LMenuBar::FindMenuItem(
  248.     CommandT    inCommand,
  249.     ResIDT        &outMENUid,
  250.     MenuHandle    &outMenuHandle,
  251.     Int16        &outItem)
  252. {
  253.     Int16    theItem = 0;            // Search menu list for the command
  254.     LMenu    *theMenu = mMenuListHead;
  255.     while (theMenu) {
  256.         theItem = theMenu->IndexFromCommand(inCommand);
  257.         if (theItem != 0) {
  258.             break;
  259.         }
  260.         theMenu = theMenu->GetNextMenu();
  261.     }
  262.     
  263.     if (theItem != 0) {                // Command found, get ID and MenuHandle
  264.         outMENUid = theMenu->GetMenuID();
  265.         outMenuHandle = theMenu->GetMacMenuH();
  266.         
  267.     } else {                        // Command not found
  268.         outMENUid = 0;
  269.         outMenuHandle = nil;
  270.     }
  271.     outItem = theItem;
  272. }
  273.  
  274.  
  275. // ---------------------------------------------------------------------------
  276. //        • FindNextCommand
  277. // ---------------------------------------------------------------------------
  278. //    Passes back the next command in the MenuBar
  279. //
  280. //    On entry,
  281. //        ioIndex, ioMenuHandle, and ioMenu specify an item in a Menu
  282. //        ioMenuHandle of nil means to start at the beginning, so the
  283. //        next command will be the first one in the MenuBar
  284. //    On exit,
  285. //        ioIndex, ioMenuHandle, and ioMenu specify the next item in
  286. //        the MenuBar. If the next item is in the same menu, ioIndex
  287. //        is incremented by one and ioMenuHandle and ioMenu are unchanged.
  288. //        If the next item is in another menu, ioIndex is one, and
  289. //        ioMenuHandle and ioMenu refer to the next menu.
  290. //        outCommand is the command number associated with that item
  291. //
  292. //    Returns true if the next command exists
  293. //    Returns false if there is no next command
  294. //
  295. //    Use this function to iterate over all commands in the MenuBar:
  296. //
  297. //        LMenuBar    *theMenuBar = LMenuBar::GetCurrentMenuBar();
  298. //        Int16        menuItem;
  299. //        MenuHandle    macMenuH = nil;
  300. //        LMenu        *theMenu;
  301. //        CommandT    theCommand;
  302. //
  303. //        while (theMenuBar->FindNextCommand(menuItem, macMenuH,
  304. //                                            theMenu, theCommand)) {
  305. //            // Do something with theCommand
  306. //        }
  307.                                     
  308. Boolean
  309. LMenuBar::FindNextCommand(
  310.     Int16        &ioIndex,
  311.     MenuHandle    &ioMenuHandle,
  312.     LMenu*        &ioMenu,
  313.     CommandT    &outCommand)
  314. {
  315.     if (ioMenuHandle == nil) {        // Special case: first time
  316.         ioIndex = 0;                //   Start at beginning of our Menu list
  317.         ioMenu = mMenuListHead;
  318.         if (ioMenu == nil) {
  319.             return false;            // Quick exit if there are no Menus
  320.         }
  321.     }
  322.         
  323.     Boolean        cmdFound;
  324.     do {
  325.                                     // Get MenuHandle for current Menu
  326.         ioMenuHandle = ioMenu->GetMacMenuH();
  327.                                     // Search in current Menu
  328.         cmdFound = ioMenu->FindNextCommand(ioIndex, outCommand);
  329.         
  330.         if (!cmdFound) {            // No next command in current Menu
  331.             ioIndex = 0;            // Move to start of next Menu
  332.             ioMenu = ioMenu->GetNextMenu();
  333.         }
  334.                                     // End search upon finding next command
  335.                                     //   or reaching end of menu list 
  336.     } while (!cmdFound && (ioMenu != nil));
  337.     
  338.     return cmdFound;
  339. }
  340.  
  341.  
  342. // ---------------------------------------------------------------------------
  343. //        • InstallMenu
  344. // ---------------------------------------------------------------------------
  345. //    Install a Menu object in the MenuBar
  346.  
  347. void
  348. LMenuBar::InstallMenu(
  349.     LMenu    *inMenuToInstall,
  350.     ResIDT    inBeforeMENUid)
  351. {
  352.         // It's possible to add a Menu twice to the Mac menu list--
  353.         // once as a regular menu and once as a submenu (hierarchical
  354.         // or popup menu). However, we only need one copy of the
  355.         // menu in our menu list.
  356.         
  357.     if (!inMenuToInstall->IsInstalled()) {
  358.                                     // Add it to our singly-linked list
  359.         inMenuToInstall->SetNextMenu(mMenuListHead);
  360.         mMenuListHead = inMenuToInstall;
  361.         inMenuToInstall->SetInstalled(true);
  362.     }
  363.     
  364.                                     // Add it to the Mac MenuBar
  365.     MenuHandle    macMenuH = inMenuToInstall->GetMacMenuH();
  366.     ::InsertMenu(macMenuH, inBeforeMENUid);
  367.         
  368.                                     // Search menu items for submenus
  369.                                     //   and install them also
  370.     Int16    itemCount = ::CountMItems(macMenuH);
  371.     for (Int16 item = 1; item <= itemCount; item++) {
  372.         Int16    itemCmd;
  373.         ::GetItemCmd(macMenuH, item, &itemCmd);
  374.         if (itemCmd == hMenuCmd) {
  375.             Int16    subMenuID;        // Submenu found. Get its ID, create
  376.                                     //   a new Menu object, and recursively
  377.                                     //   call this function to install it.
  378.                                     //   Recursion means that sub-submenus
  379.                                     //   get installed too.
  380.             ::GetItemMark(macMenuH, item, &subMenuID);
  381.             
  382.                 // Creating an LMenu for a menu ID that does not exist will
  383.                 // throw an exception. If this happens, we should just
  384.                 // continue. Users wishing to know about this (it's most
  385.                 // likely a typo in the MENU resource or a missing MENU
  386.                 // for the submenu) should set debug_Throw appropriately.
  387.                 //
  388.                 // However, some extensions dynamically insert hierarchical
  389.                 // menus by patching _InsertMenu. In these cases, it's OK
  390.                 // not to find the submenu.
  391.                 
  392.             try {
  393.                 InstallMenu(new LMenu(subMenuID), hierMenu);
  394.             }
  395.             
  396.             catch (ExceptionCode inErr) { }
  397.         }
  398.     }
  399.  
  400.     if (inBeforeMENUid != hierMenu) {
  401.                                     // Not a submenu, so force a redraw
  402.         ::InvalMenuBar();            //   of the MenuBar
  403.     }
  404. }
  405.  
  406.  
  407. // ---------------------------------------------------------------------------
  408. //        • RemoveMenu
  409. // ---------------------------------------------------------------------------
  410. //    Remove a Menu object from the MenuBar
  411.  
  412. void
  413. LMenuBar::RemoveMenu(
  414.     LMenu    *inMenuToRemove)
  415. {
  416.                                     // Search for Menu in our list
  417.     LMenu    *theMenu = mMenuListHead;
  418.     LMenu    *prevMenu = nil;
  419.     while ((theMenu != nil) && (theMenu != inMenuToRemove)) {
  420.         prevMenu = theMenu;
  421.         theMenu = theMenu->GetNextMenu();
  422.     }
  423.     
  424.     if (theMenu != nil) {            // Menu is in our list
  425.                                     // Remove it from our singly-linked list
  426.         if (prevMenu == nil) {
  427.             mMenuListHead = inMenuToRemove->GetNextMenu();
  428.         } else {
  429.             prevMenu->SetNextMenu(inMenuToRemove->GetNextMenu());
  430.         }
  431.         inMenuToRemove->SetNextMenu(nil);
  432.         inMenuToRemove->SetInstalled(false);
  433.                                     
  434.                                     // Remove it from the Mac MenuBar
  435.         ::DeleteMenu(inMenuToRemove->GetMenuID());
  436.         ::InvalMenuBar();                // Force redraw of MenuBar
  437.                                     // ??? don't redraw if a submenu ???
  438.     }
  439. }
  440.  
  441.  
  442. // ---------------------------------------------------------------------------
  443. //        • FetchMenu
  444. // ---------------------------------------------------------------------------
  445. //    Return the Menu object for the specified MENU resource ID
  446. //
  447. //    Returns nil if there is no such Menu object
  448.  
  449. LMenu*
  450. LMenuBar::FetchMenu(
  451.     ResIDT    inMENUid) const
  452. {
  453.     LMenu    *theMenu = mMenuListHead;
  454.                                     // Search menu list until reaching the
  455.                                     //   end or finding a match
  456.     while ((theMenu != nil) && (theMenu->mMENUid != inMENUid)) {
  457.         theMenu = theMenu->GetNextMenu();
  458.     }
  459.     return theMenu;
  460. }
  461.  
  462.  
  463. // ---------------------------------------------------------------------------
  464. //        • FindNextMenu
  465. // ---------------------------------------------------------------------------
  466. //    Pass back the next Menu in a MenuBar
  467. //
  468. //    On entry, ioMenu is a pointer to a Menu object
  469. //        Pass nil to get the first Menu
  470. //    On exit, ioMenu is a pointer to the next Menu in the MenuBar
  471. //        ioMenu is nil if there is no next menu
  472. //
  473. //    Returns false if there is no next menu
  474. //
  475. //    Use this function to loop thru all the Menus in a MenuBar:
  476. //        LMenuBar    *theMenuBar = LMenuBar::GetCurrentMenuBar();
  477. //        LMenu    *theMenu = nil;
  478. //        while (theMenuBar->FindNextMenu(theMenu) {
  479. //            // ... do something with theMenu
  480. //        }
  481.  
  482. Boolean
  483. LMenuBar::FindNextMenu(
  484.     LMenu*    &ioMenu) const
  485. {
  486.     if (ioMenu == nil) {
  487.         ioMenu = mMenuListHead;
  488.         
  489.     } else {
  490.         ioMenu = ioMenu->GetNextMenu();
  491.     }
  492.     
  493.     return (ioMenu != nil);
  494. }